$$ \newcommand{\floor}[1]{\left\lfloor{#1}\right\rfloor} \newcommand{\ceil}[1]{\left\lceil{#1}\right\rceil} \renewcommand{\mod}{\,\mathrm{mod}\,} \renewcommand{\div}{\,\mathrm{div}\,} \newcommand{\metar}{\,\mathrm{m}} \newcommand{\cm}{\,\mathrm{cm}} \newcommand{\dm}{\,\mathrm{dm}} \newcommand{\litar}{\,\mathrm{l}} \newcommand{\km}{\,\mathrm{km}} \newcommand{\s}{\,\mathrm{s}} \newcommand{\h}{\,\mathrm{h}} \newcommand{\minut}{\,\mathrm{min}} \newcommand{\kmh}{\,\mathrm{\frac{km}{h}}} \newcommand{\ms}{\,\mathrm{\frac{m}{s}}} \newcommand{\mss}{\,\mathrm{\frac{m}{s^2}}} \newcommand{\mmin}{\,\mathrm{\frac{m}{min}}} \newcommand{\smin}{\,\mathrm{\frac{s}{min}}} $$

Prijavi problem


Obeleži sve kategorije koje odgovaraju problemu

Još detalja - opišite nam problem


Uspešno ste prijavili problem!
Status problema i sve dodatne informacije možete pratiti klikom na link.
Nažalost nismo trenutno u mogućnosti da obradimo vaš zahtev.
Molimo vas da pokušate kasnije.

C++
C#

Аритметика

У овој глави приказаћемо како се могу решавати разни задаци у којима се врше основна аритметичка израчунавања. Такви задаци се обично срећу у математици, физици и хемији, а коришћење рачунара олакшава њихово решавање, јер је потребно само записати формуле на основу којих се долази до решења, док је рачунар тај који преузима посао извршавања рачунских операција.

Елементи програмског језика

Пре задатака укратко ћемо описати елементе програмског језика C++ које ћемо користити у решењима у овој глави.

Основна структура програма

Опишимо за почетак једноставан програм који на екран исписује поруку Zdravo svete!.

#include <iostream>
using namespace std;

int main() {
  // na ekran ispisujemo pozdravnu poruku
  cout << "Zdravo svete!" << endl;
  return 0;
}

Покушај да овај програм прекуцаш, преведеш и покренеш. Обрати пажњу на то да језик C++ прави разлику између малих и великих слова и врло је важно да ли је нешто написано малим или великим словом.

Централни део програма чини следећи програмски кôд:

// na ekran ispisujemo pozdravnu poruku
cout << "Zdravo svete!" << endl;

Линија cout << "Zdravo svete!" << endl; представља једну наредбу којом се на екран исписује поздравна порука Zdravo svete (без двоструких наводника), након чега се прелази у нови ред. Објекат cout (од енглеског “console output”) представља стандардни излаз нашег програма, што је најчешће екран, и у њега се “улива” прво текст Zdravo svete, а затим и прелазак у нови ред који се означава са endl (од енглеског “end line”). Ово “уливање” је представљено симболима << (текст “тече” и улива се на cout, па се cout назива и стандардни излазни ток).

Линија // na ekran ispisujemo pozdravnu poruku је коментар и он служи да ономе ко чита овај програм објасни шта се постиже наредном линијом (та линија није битна рачунару за превођење и извршавање овог програма, па је он занемарује). Да би се добио програм који може да се преведе и изврши, овај централни део програма потребно је окружити још неким линијама чије ћемо значење покушати сада да објаснимо.

Сви програми морају да садрже функцију main тј. у коду је потребно да постоји део облика

int main() {
   // ovde se navode naredbe našeg programa
   return 0; // ovim se signalizira da je program uspešno izvršen
}

Линијом int main() започињемо дефиницију функције main. О функцијама ће бити више речи касније, оно што је сада битно да знаш јесте то да је функција main главна функција и извршавање сваког C++ програма почиње извршавањем наредби наведених у оквиру ове функције. Део функције између витичастих заграда назива се тело функције. Тело функције садржи наредбе које се извршавају када се позове та функција. Када корисник покрене програм, тада оперативни систем позове функцију main тог програма и крене се са извршавањем наредби наведених у њеном телу. Последња наредба у функцији main је најчешће return 0; којом наш програм оперативном систему враћа вредност 0 и тиме јавља да је његово извршавање успешно завршено. Ако функција врати неку вредност различиту од 0 то може означавати да је дошло до неке грешке приликом извршавања програма, но у овој збирци се нећемо фокусирати на могуће грешке (претпостављаћемо да је увек све у реду), па ће сви наши програми увек само враћати вредност 0.

У првој линији програма директивом #include <iostream> у наш програм укључујемо бибилотеку за рад са улазно-излазним токовима. То између осталог значи да у нашем програму можемо да неки текст испишемо на екран, да неке вредности учитамо са тастатуре и слично. У овом програму користили смо cout и endl који користимо када желимо да пређемо у нови ред. Да нисмо навели #include <iostream>, добили бисмо поруку о томе да преводилац нашег програма не разуме шта су cout и endl. Пошто ће сваки програм који будемо писали писати нешто по екрану, сваки ће почињати директивом #include <iostream>.

Након директиве include наведена је директива using namespace std;. Она омогућава да се сви елементи стандардне библиотеке користе без префикса std::. На пример, излазни ток се означава са cout, те уместо да свуда пишемо да је он део стандардне библиотеке std, тј. да пишемо std::cout можемо једноставније само писати cout. Да нисмо навели using namespace std;, тада би централни део нашег програма морао да буде написан у наредном облику.

// na ekran ispisujemo pozdravnu poruku
std::cout << "Zdravo svete!" << std::endl;

Коментари

Већ смо рекли да у коду можемо писати коментаре – текст којим се објашњава шта се у неком делу програма ради и који је намењен ономе ко буде читао програм (или ономе ко је тај програм писао, ако некада касније буде потребе да га доради или преправи). Коментаре рачунар игнорише приликом превођења програма. У језику C++ коментар почиње навођењем ознаке // и простире се до краја тог реда (овакве коментаре често називамо линијским коментарима). Можемо писати и коментаре који се протежу кроз неколико суседних редова (тзв. вишелинијске коментаре) и они почињу ознаком /*, а завршавају се ознаком */. У овој збирци ћемо се трудити да наводимо коментаре што више (често много више него што је то уобичајена пракса), да бисмо вам помогли у разумевању приложених решења.

Испис, учитавање, променљиве, типови

Већ смо видели да се испис на стандардни излаз (то је најчешће екран рачунара тј. такозвана конзола) врши наредбом облика cout << "...";, при чему се текст који се исписује наводи између двоструких наводника.

У једном програму је могуће навести и више оваквих наредби (обично наведених један испод другог). На пример,

#include <iostream>
using namespace std;

int main() {
  cout << "Ucim da programiram.";
  cout << "Koristim C++.";
  cout << "Zdravo svima!";
  return 0;
}

Иако текст програма не мора бити сложен овако уредно (наредбе су увучене, поравнате једна испод друге), то је веома пожељно. Када се програм покрене, иако су наредбе сложене једна испод друге, наведене реченице се исписују једна до друге.

Ucim da programiram.Koristim C++.Zdravo svima!

Исти ефекат би се постигао навођењем једне наредбе исписа облика:

cout << "Ucim da programiram." << "Koristim C++." << "Zdravo svima!";

или мало другачије сложено

cout << "Ucim da programiram." 
     << "Koristim C++."
     << "Zdravo svima!";

Ако се жели да се након исписа текста пређе у нови ред, онда је потребно након ниске под двоструким наводницима исписати и знак за прелаз у нови ред endl. На пример, функција

int main() {
  cout << "Ucim da programiram." << endl;
  cout << "Koristim C++." << endl;
  cout << "Zdravo svima!" << endl;
  return 0;
}

исписује реченице једну испод друге:

Ucim da programiram. Koristim programski jezik C++. Zdravo svima!

Текст може да се учита од корисника. Размотримо наредни програм.

#include <iostream>
using namespace std;

int main() {
  cout << "Kako se zoves?" << endl;
  string ime;
  cin >> ime;
  cout << "Zdravo, ti se zoves " << ime << endl;
  return 0;
}

Наредбом cout << "Kako se zoves?" << endl; на екран исписујемо текст Kako se zoves?, што је веома слично првом програму који смо анализирали.

Након тога желимо да корисник унесе своје име. Текст који корисник унесе морамо негде да упамтимо, да бисмо га касније исписали. Да бисмо упамтили разне вредности (у овом примеру то је текст који је корисник унео, а у наредним примерима ће то бити разни бројеви са којима ћемо вршити различита израчунавања) користимо променљиве. У наведеном примеру користимо променљиву под називом ime и у њу смештамо текст који је корисник унео. Променљиве можемо замислити као кутијице у којима се чувају различите вредности. У сваком тренутку стара вредност може бити избачена из кутијице и у кутијицу може бити уписана нека нова вредност. C++ спада у групу такозваних статички типизираних језика што значи да се за сваку променљиву унапред зна тип вредности који се у њој може чувати. У неким променљивама можемо да чувамо само текст, у другима само целе бројеве, у трећим само реалне бројеве и слично. Приликом првог увођења неке променљиве у наш програм, дужни смо да наведемо њен тип. Декларација подразумева увођење имена нове променљиве и придруживање типа. У претходном примеру декларација је била линија string ime; Њоме смо декларисали променљиву под називом ime и рекли да ће она бити типа string, тј. да ће се у њој чувати текст.

Наредбом cin >> ime учитавамо текст (једну ниску карактера) од корисника. Објекат cin (од енглеског “console input”) означава стандардни улаз који најчешће одговара тастатури. Наравно, очекујемо да корисник унесе своје име (мада може да унесе шта год жели - наш програм то неће приметити). Подаци опет “теку”, једино што овај пут теку са улаза тј. са тастатуре у променљиву ime (што је наглашено симболима >>). Зато се и cin назива стандардни улазни ток.

На крају, наредбом cout << "Zdravo, ti se zoves " << ime << endl, на стандардни излаз исписујемо прво текст Zdravo, ti se zoves, затим садржај променљиве ime (то је текст који је корисник унео) и на крају прелазимо у нови ред.

У претходном примеру видели смо како се ради са текстуалним типом података string. У првим програмима у овој збирци користићемо следеће основне типове података.

тип опис пример
string текст (ниска карактера) "Zdravo"
int цео број 1234
double реалан број 3.141
bool логичка вредност true или false

Иако треба од почетка имати на уму да променљиве не могу да чувају произвољно велике тј. произвољно мале бројеве, у почетку нећемо обраћати превише пажње на то (на пример, у променљивој типа int најчешће се могу чувати вредности од око минус две милијарде, па све до око две милијарде, што је сасвим довољно у свим почетним задацима). Слично важи за податке типа double (и овај тип има ограничени распон и прецизност тј. број децимала).

Првобитна додела вредности променљивој назива се иницијализација. Приликом доделе могуће је променљивама додељивати и неке конкретне вредности (кажемо константе). На пример

string ime = "Pera";

Пре него што се текстулна променљива иницијализује њена вредност је празан текст (текст "").

Рецимо да именовање променљивих треба да тече у складу са њиховим значењем (пожељно је избегавати кратка, неинформативна имена попут a, b, x, y, осим ако из контекста програма није потпуно јасно шта би те променљиве могле да означавају). Имена променљивих не смеју да садрже размаке и морају бити састављена само од слова, цифара и доње црте тј. подвлаке (карактера _), али не могу почињати цифром.

Прикажимо сада примере декларација и иницијализација вредности других типова. На пример, наредном декларацијом се у програм уводе две променљиве под називом x и y и каже се да ће оне чувати целобројне вредности.

int x, y;

Приметимо да смо у претходном примеру једном декларацијом увели две променљиве, што је краће него да смо писали посебно две декларације.

int x;
int y;

Наравно, и целобројне променљиве могу бити иницијализоване.

int x = 1, y = 2;
int a = 3, b, c = 4;

У претходном примеру је декларисано пет променљивих, а иницијализоване су четири.

Када су у питању реалне вредности, морамо нагласити да се оне наводе са децималном тачком (у складу са правописом енглеског језика), а не запетом (у складу са правописом српског језика).

double pi = 3.14159265;

Наредба исписа коју смо раније видели може бити употребљена и за испис бројевних, па и логичких вредности. На пример, наредним наредбама

cout << 123 << endl;
int x = 5;
cout << x << endl;
cout << 12.345 << endl;
double pi = 3.14159265;
cout << pi << endl;

исписује се

123 5 12.345 3.14159265

Приликом исписа реалних бројева могуће је заокружити их на одређени број децимала. То се може постићи коришћењем функције setprecision(n), где је са \(n\) означен број децимала који желимо да прикажемо. Ова функција је декларисана у заглављу iomanip те је на почетку програма потребно навести директиву #include <iomanip>. Квалификатор fixed поставља да се сви бројеви у покретном зарезу приказују у фиксном децималном формату. Да би се приказивале и потенцијалне крајње нуле у приказу броја са фиксним бројем децимала користимо квалификатор showpoint. Сви ови квалификатори примењују се на ток cout у комбинацији са оператором исписа, те бисмо на пример број \(\pi\) могли да прикажемо на 4 децимале на следећи начин:

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    double pi = 3.14159265;
    cout << fixed << showpoint << setprecision(4) << pi << endl;
    return 0;
}

Овим добијамо исписан резултат 3.1416.

Напоменимо и да је приликом исписа могуће комбиновати текст и бројевне вредности. На пример,

double pi = 3.1415926;
cout << "Vrednost broja pi je " << pi << endl;

Поред текста, са стандардног улаза можемо учитавати и бројеве. Размотримо наредни програм.

cout << "Koliko imaš godina?" << endl;
int brGodina;
cin >> brGodina;
cout << "Zdravo, ti imaš " << brGodina << " godina." << endl;

Након учитавања једног целог броја са тастатуре, кориснику се исписује одговарајућа порука.

Уколико је потребно унети више вредности са тастатуре (на пример, дан и месец рођења), онда се у једној наредби може надовезати учитавање свих потребних вредности. Притом је те две вредности могуће унети у једној линији раздвојене размаком или у две засебне линије.

cout << "Kad si rodjen?" << endl;
int dan, mesec;
cin >> dan >> mesec;
cout << "Zdravo, ti si rodjen " << dan << ". " << mesec << "." << endl;

Основне аритметичке операције и изрази

Ниједан од програма које смо до сада срели није могао бити нарочито интересантан. Могли смо да учитамо податке и да их испишемо у неизмењеном облику.

Рачунар је машина која обрађује податке, тј. која примењујући рачунске операције на основу улазних података добија излазне. У рачунару је све записано помоћу бројева и на најнижем нивоу се све операције своде на основне операције над бројевима. Рачунар или компјутер (енгл. computer) је справа која рачуна тј. справа која је направљена тако да може веома брзо и ефикасно да изводи рачунске операције над бројевима. Рачунање се назива и аритметика (од грчке речи ἀριθμός тј. аритмос која значи број, бројање, рачунање), а рачунске операције се називају и аритметичке операције.

  • Основна аритметичка операција је сабирање. Збир бројева \(3\) и \(5\) се у математици представља као \(3 + 5\). У програмском језику C++ користи се идентичан запис 3 + 5. Сабирање је применљиво и на целе и на реалне бројеве. На пример, кôд који учитава и сабира два цела броја може бити написан на следећи начин.

        int x, y;
        cin >> x >> y;
        cout << x + y << endl;
  • Поред сабирања можемо разматрати одузимање. Разлика бројева \(8\) и \(2\) се у математици представља као \(8 - 2\). У програмском језику C++ користи се идентичан запис \(8 - 2\). Одузимање је применљиво и на целе и на реалне бројеве.

  • Још једна од основних операција је и множење. Производ бројева \(4\) и \(6\) се у математици представља као \(4 \cdot 6\). У програмском језику C++ множење се означава помоћу оператора * и производ бројева 4 и 6 се записује као 4 * 6.

  • У програмском језику C++, наравно, можемо и да делимо, да израчунавамо остатак при дељењу и цео део количника. Дељење реалних бројева се врши помоћу оператора / и количник бројева 7,2 и 6,4 се записује помоћу 7.2 / 6.4. Дељењем два цела броја добија се њихов целобројни количник, док се остатак при дељењу два цела броја може израчунати оператором %. На пример, вредност израза 14 / 4 једнака је 3, а израза 14 % 4 једнака је 2. Ако желимо да одредимо реални количник два цела броја, морамо их представити у реалном облику (на пример, уместо 14/4 пишемо 14.0/4.0). У случају да се деле целобројне променљиве онда вршимо експлицитну конверзију у реалне вредности навођењем (double) испред назива променљиве (нпр. уместо x / y пишемо (double) x / (double) y. Нагласимо да је довољно да било дељеник било делилац буду реални да би се применило реално дељење. О дубљим везама између реалног и целобројног типа биће више речи у каснијим поглављима.

Слично као и у математици, од константних вредности и променљивих, применом оператора и заграда граде се изрази. Приоритет оператора је усклађен са уобичајеним приоритетом у математици, па је приоритет оператора *, / и % виши од приоритета оператора + и -, док сви наведени оператори имају леву асоцијативност (рачунске операције се изводе с лева на десно). Приоритет и асоцијативност се могу применити навођењем заграда.

У првим програмима ћемо се трудити да приликом извођења операција не мешамо податке различитог типа. Ипак, нагласимо да ако је у изразу један број реалан, а други цео, пре извођења операција се тај цео број претвара у реалан и операција се извршава над реалним бројевима.

Уграђене математичке функције

У многим конкретним применама поред основних аритметичких операција примењују се и неке мало напредније математичке функције и неке чувене математичке константе (нпр. \(\pi\)). Да бисмо их користили у језику C++, потребно је на почетку програма укључити заглавље <cmath> директивом #include <cmath>. Функције које ће ти бити потребне у наредним задацима су:

  • pow(x, y) - израчунава степен \(x^y\), при чему се може применити и за израчунавање корена (не само квадратних) знајући да је \(\sqrt[n]{x} = x^{\frac{1}{n}}\);
  • sqrt(x) - израчунава квадратни корен \(\sqrt{x}\);
  • abs(x) - израчунава апсолутну вредност \(|x|\);

Напоменимо да је у заглављу <cmath> декларисана функција за израчунавање апсолутне вредности реалног броја, док је одговарајућа функција за целе бројеве декларисана у заглављу <cstdlib>.

Што се константе \(\pi\) тиче, она у језику C++ није стандардизована. Већина компилатора подржава коришћење константе M_PI, међутим, да би се та константа могла употребити, код неких компилатора потребно је на почетку програма (пре директива include) навести дефиницију

#define _USE_MATH_DEFINES

и тек након ње укључити заглавље <cmath>. Нагласимо још једном да је наведену дефиницују у програму потребно навести пре свих укључивања заглавља, јер се може десити и да неко друго заглавље индиректно укључи заглавље <cmath>.

Поред ових на располагању су нам и тригонометријске функције (синус, косинус, …), њихове инверзне функције, логаритамска функција, експоненцијална функција и слично, али њих нећемо користити у почетним задацима.

Именоване константе

Вредности које су нам потребне у програму, а које се неће мењати током извршавања програма можемо дефинисати у виду именованих константи, које се дефинишу као обичне променљиве, уз навођење кључне речи const пре типа податка, уз обавезну иницијализацију вредности на неку константну вредност. На пример:

const double PI = 3.14159265;

Именованим константама није касније могуће променити вредност у програму.

Дефинисање функција

Поред библиотечких функција које су нам на располагању, програмски језици, па и језик C++ програмеру дају могућност да дефинише функције, што доприноси избегавању поновљених делова програма, декомпозицији проблема на мање потпроблеме и бољој организацији кода. Функције можемо замислити слично као у математици. Оне обично за неколико улазних параметара израчунавају једну резултујућу вредност. На пример, наредна функција израчунава обим правоугоника датих страница.

#include <iostream>
using namespace std;

// funkcija izračunava obim pravougaonika na osnovu poznatih
// dužina stranica a i b
double obim(double a, double b) {
  // formula za obim pravougaonika
  return 2*a + 2*b;
}

int main() {
  double a, b;
  // učitavamo dužine stranica pravougaonika
  cin >> a >> b;
  // izračunavamo obim pomoću definisane funkcije
  double O = obim(a,b);
  // ispisujemo izračunati obim
  cout << O << endl;
  return 0;
}

Прва линија double obim(double a, double b) се назива декларација функције и у њој се каже да се функција назива obim, да прима две улазне вредности (два параметра) који су реални бројеви и називају се a и b и да враћа резултат који је такође реалан број. У телу функције (оно је ограничено витичастим заградама) се описује како се резултат израчунава на основу улазних вредности. Када је коначни резултат израчунат, он се враћа кључном речју return. Након исправне дефиниције следи позив функције. У претходном примеру позив је извршен у склопу декларације double O = obim(a, b). У позиву се наводе аргументи којима се иницијализује вредност параметара. У овом случају то су вредности променљивих које су учитане са тастатуре. Аргументи могу бити и константне вредности (параметри морају бити променљиве). На пример, допуштен је позив obim(5.0, 7.0).

Унутар функције можемо декларисати и користити и променљиве у којима чувамо међурезултате. На пример, можемо дефинисати функцију за израчунавање површине једнакостраничног троугла дате дужине странице.

double PovrsinaJednakostranicnogTrougla(double a) {
   // izračunavamo visinu trougla
   double h = a * sqrt(3) / 2.0;
   // izračunavamo površinu na osnovu dužine stranice i visine
   double P = a * h / 2.0;
   // vraćamo konačan rezultat
   return P;
}

Овом функцијом је дефисан начин да се израчуна површина једнакостраничног троугла и када нам год то надаље затреба у програму (а у неком математичком задатку то може бити потребно више пута), можемо само позвати функцију и добити жељени резултат.

Постоје и специјалне функције које не враћају вредност. Оне се некада називају процедурама и једини задатак им је да произведу неки пропратни ефекат (на пример, да нешто испишу на екран). Као повратни тип података наводи се void. На пример, можемо дефинисати процедуру која око датог текста исписује украсне линије, а онда је позвати неколико пута у програму.

#include <iostream>
using namespace std;

void ukrasiTekst(string tekst) {
  cout << "---------------------------------------" << endl;
  cout << tekst << endl;
  cout << "---------------------------------------" << endl;
}

int main() {
  ukrasiTekst("Dobar dan!");
  ukrasiTekst("Zdravo, svima!");
  ukrasiTekst("Dovidjenja!");
  return 0;
}

Више повратних вредности

Параметри функције се по правилу сматрају њеним улазним вредностима (исто као и у математици), док се вредност враћена помоћу return сматра њеном излазном вредношћу. Функција, дакле, може имати више улазних и само једну излазну вредност (и ово одговара појму функције у математици). Међутим, у програмирању некада имамо потребу да дефинишемо функције које враћају више од једне вредности. То је могуће урадити у језику C++ тако што параметре преносимо по референци (непосредно након назива типа наведемо карактер &). Након тога могуће је вредности тих параметара променити унутар функције и та измена ће важити и након завршетка рада функције. На пример, наредна функција конвертује дужину задату само у центиметрима у дужину задату у метрима и центиметрима.

void uMetreICentimetre(int duzina, int& metri, int& centimetri) {
   metri = duzina / 100;
   centimetri = duzina % 100;
}

Позив ове функције се врши на следећи начин.

int metri, centimetri;
uMetreICentimetre(273, metri, centimetri);

Након овог позива, вредност променљиве metri биће 2, а центиметри биће 73. Параметри који се преносе по референци су улазно-излазни параметри функције.

Аритметика

У овој глави приказаћемо како се могу решавати разни задаци у којима се врше основна аритметичка израчунавања. Такви задаци се обично срећу у математици, физици и хемији, а коришћење рачунара олакшава њихово решавање, јер је потребно само записати формуле на основу којих се долази до решења, док је рачунар тај који преузима посао извршавања рачунских операција.

Елементи програмског језика

Пре задатака укратко ћемо описати елементе програмског језика C# које ћемо користити у решењима у овој глави.

Основна структура програма

Опишимо за почетак једноставан програм који на екран исписује поруку Zdravo svete!.

using System;

class Program
{
   static void Main()
   {
       Console.WriteLine("Zdravo svete!");
   }
}

Обратимо пажњу на то да језик C# прави разлику између малих и великих слова и врло је важно да ли је нешто написано малим или великим словом (на пример, погрешно би било написати реч using великим словима или реч Main малим словима).

У првој линији директивом using System; наглашавамо да ћемо користити услуге стандардне библиотеке језика C#. Стандардна библиотека сваког језика пружа одређене функционалности програмерима. Неке од основних функционалности сваког конзолног програма је да се неки текст испише на екран (у конзолу), да се неки текст учита са тастатуре и слично. Пошто ће сваки програм који будемо писали користити ове функционалности, сваки ће почињати директивом using System;. Рецимо и да шкољке програма које генерише Visual Studio имају мало богатију структуру, па и богатију листу using директива на почетку.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

Већина библиотека које су укључене неће бити потребне у почетним програмима тако да се осим прве, све наредне using директиве слободно могу обрисати, али није никаква грешка и оставити их. Ми их у програмима нећемо наводити, а ви не морате да их бришете ако користите Visual Studio.

Након директиве using следи дефиниција класе под називом Program. Сваки C# програм има бар једну класу у којој је дефинисана статичка функција под називом Main. Иако претходна реченица сигурно у овом тренутку звучи прилично неразумљиво, на то се не морамо превише обазирати, због тога што ће сви програми које будемо писали имати потпуно исту структуру. Такође, окружење Visual Studio ту шкољку програма генерише уместо нас, тако да ће наш задатак у почетним програмима бити само да дописујемо програмски кôд између витичастих заграда након линије у којој се појављује реч Main.

using System;

class Program
{
   static void Main()
   {
       // ovde se upisuje programski kod našeg programa
   }
}

Када се конзолна апликација креира из окружења Visual Studio, генерисани кôд је још мало компликованији. Класа је смештена у такозвани именски простор (енгл. namespace) који носи подразумевано име наше конзолне апликације (нпр. ConsoleApplication1), међутим, то нам поново није релевантно у почетним програмима и опет ће нам у почетку бити само битно да упишемо наредбе нашег програма у функцију Main.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
   class Program
   {
       static void Main(string[] args)
       {
           // ovde se upisuje programski kod našeg programa
       }
   }
}

Коментари

У коду можемо писати коментаре – текст којим се објашњава шта се у неком делу програма ради и који је намењен ономе ко буде читао програм (или ономе ко је тај програм писао, ако некада касније буде потребе да га доради или преправи). Коментаре рачунар игнорише приликом превођења програма. У језику C# коментар почиње навођењем ознаке // и простире се до краја тог реда (овакве коментаре често називамо линијским коментарима). Можемо писати и коментаре који се протежу кроз неколико суседних редова (тзв. вишелинијске коментаре) и они почињу ознаком /*, а завршавају се ознаком */. У овој збирци ћемо се трудити да наводимо коментаре што више (често много више него што је то уобичајена пракса), да бисмо вам помогли у разумевању приложених решења.

Испис, учитавање, променљиве, типови

Испис у конзолу се врши наредбом Console.WriteLine("..."), при чему се текст који се исписује наводи између двоструких наводника. У једном програму може бити и више оваквих наредби (обично наведених једна испод друге). На пример,

using System;

class Program
{
   static void Main()
   {
       Console.WriteLine("Učim da programiram.");
       Console.WriteLine("Koristim programski jezik C#.");
       Console.WriteLine("Zdravo svima!");
   }
}

Иако текст програма не мора бити сложен овако уредно (наредбе су увучене, поравнате једна испод друге), то је веома пожељно. Едитор Visual Studio нам у томе помаже. Ако се случајно догоди да се програм испомера, Visual Studio може сам да га поравна (потребно је из менија покренути опцију EditAdvancedFormat Document или поравнавање покренути пречицом на тастатури ctrl + k, ctrl + d).

Када се програм покрене, наведене линије се исписују једна испод друге.

Učim da programiram. Koristim programski jezik C#. Zdravo svima!

То би било тако чак и да су у тексту програма наредбе навођене једна поред друге. Ако се жели да се након исписа текста остане у истом реду, онда је уместо функције Console.WriteLine потребно употребити функцију Console.Write. Тако програм

using System;

class Program
{
   static void Main()
   {
       Console.Write("Učim da programiram ");
       Console.WriteLine("u programskom jeziku C#.");
   }
}

исписује текст

Učim da programiram u programskom jeziku C#.

После прве наредбе се није прешло у наредни ред, па се наставак реченице појавио у истом реду, непосредно након размака који је наведен као последњи карактер у тексту који се исписује првом наредбом.

Веома интересантно, текст се у програмском језику C# може “сабирати” слично као што се у математици сабирају бројеви. Уместо сабирања, наравно, врши се надовезивање текста. Тако је збир "Zdravo" + " " + "svete" + "!" једнак тексту "Zdravo svete!". Ово се може користити приликом исписа. Наредба

Console.WriteLine("Zdravo" + " " + "svete" + "!");

проузрокује испис текста

Zdravo svete!

Текст може да се учита од корисника. Размотримо наредни програм.

using System;

class Program 
{
   static void Main()
   {
      Console.WriteLine("Kako se zoveš?");
      string ime;
      ime = Console.ReadLine();
      Console.WriteLine("Zdravo, ti se zoveš " + ime);
   }
}

У првој наредби, помоћу функције Console.WriteLine на екран исписујемо текст Kako se zoveš?. У трећој наредби помоћу функције Console.ReadLine() учитавамо једну линију текста од корисника. Наравно, очекујемо да корисник унесе своје име (мада може да унесе шта год жели - наш програм то неће приметити). Текст који корисник унесе морамо негде да упамтимо, да бисмо га у четвртој линији исписали на екран (опет помоћу функције Console.WriteLine). Да бисмо упамтили разне вредности (у овом примеру то је текст који је корисник унео, а у наредним примерима ће то бити разни бројеви са којима ћемо вршити различита израчунавања) користимо променљиве. У наведеном примеру користимо променљиву под називом ime и у њу смештамо текст који је корисник унео. Променљиве можемо замислити као кутијице у којима се чувају различите вредности. У сваком тренутку стара вредност може бити избачена из кутијице и у кутијицу може бити уписана нека нова вредност. C# спада у групу такозваних статички типизираних језика што значи да се за сваку променљиву унапред зна тип вредности који се у њој може чувати. У неким променљивама можемо да чувамо само текст, у другима само целе бројеве, у трећим само реалне бројеве и слично. Приликом првог увођења неке променљиве у наш програм, дужни смо да наведемо њен тип. У првим програмима користићемо следеће основне типове података.

тип опис пример
string текст (ниска карактера) "Zdravo"
int цео број 1234
double реалан број 3.141
bool логичка вредност true или false

Иако треба од почетка имати на уму да променљиве не могу да чувају произвољно велике тј. произвољно мале бројеве, у почетку нећемо обраћати превише пажње на то (на пример, у променљивој типа int могу се чувати вредности од око минус две милијарде, па све до око две милијарде, што је сасвим довољно у свим почетним задацима). Слично важи за податке типа double (и овај тип има ограничени распон и прецизност тј. број децимала).

Декларација подразумева увођење имена нове променљиве и придруживање типа. У претходном примеру декларација је била линија string ime;. Њоме смо декларисали променљиву под називом ime и рекли да ће она бити типа string, тј. да ће се у њој чувати текст. Након тога је следила наредба доделе у којој се текст који је корисник унео са тастатуре уписао у ту променљиву (убацио у кутијицу). Програм смо могли скратити тиме што бисмо декларацију и доделу вредности обавили у једној линији. Првобитна додела вредности променљивој назива се иницијализација.

string ime = Console.ReadLine();

Наравно, приликом доделе могуће је променљивама додељивати и неке конкретне вредности (кажемо константе). На пример

string ime = "Pera";

Ако се текстуална променљива не иницијализује, њена вредност је празан текст (текст "").

Рецимо да именовање променљивих треба да тече у складу са њиховим значењем (пожељно је избегавати кратка, неинформативна имена попут a, b, x, y, осим ако из контекста програма није потпуно јасно шта би те променљиве могле да означавају). Имена променљивих не смеју да садрже размаке и морају бити састављена само од слова, цифара и доње црте тј. подвлаке (карактера _), али не могу почињати цифром.

Прикажимо сада примере декларација и иницијализација вредности других типова. На пример, наредном декларацијом се у програм уводе две променљиве под називом x и y и каже се да ће оне чувати целобројне вредности.

int x, y;

Приметимо да смо у претходном примеру једном декларацијом увели две променљиве, што је краће него да смо писали посебно две декларације.

int x;
int y;

Наравно, и целобројне променљиве могу бити иницијализоване.

int x = 1, y = 2;
int a = 3, b, c = 4;

У претходном примеру је декларисано пет променљивих, а иницијализоване су четири.

Када су у питању реалне вредности, морамо нагласити да се оне наводе са децималном тачком (у складу са правописом енглеског језика), а не запетом (у складу са правописом српског језика).

double pi = 3.14159265;

Наредба исписа коју смо остваривали позивом функције Console.WriteLine или Console.Write може бити употребљена и за испис бројевних, па и логичких вредности. На пример, наредним наредбама

Console.WriteLine(123);
int x = 5;
Console.WriteLine(x);
Console.WriteLine(12.345);
double pi = 3.14159265;
Console.WriteLine(pi);

исписује се

123 5 12.345 3.14159265

Приликом исписа реалних бројева могуће је заокружити их на одређени број децимала. Један начин да се то уради је да се пре исписа претворе у текст, функцијом ToString, на следећи начин.

double pi = 3.14159265;
Console.WriteLine(pi.ToString("0.00"));

Овим добијамо исписан резултат 3.14. Број наведених нула одређује број децималних места у резултату.

Учитавање бројева није директно подржано библиотеком језика C#. Кориснички унос се увек третира као текст (ниска карактера), а ако представља исправан запис целог или реалног броја, он се може накнадно конвертовати у бројевну вредност. Један од начина да се текст конвертује у целобројну вредност је функција int.Parse, а у реалну вредност је функција double.Parse. Тако ћемо целе и реалне бројеве уносити на следећи начин.

int x = int.Parse(Console.ReadLine());
double y = double.Parse(Console.ReadLine());

Рецимо и то да програмски језик C# има могућност да сам погоди тип променљиве на основу вредности којом се иницијализује (тада се уместо типа променљиве у декларацији наводи реч var), али то својство нећемо често користити.

var x = int.Parse(Console.ReadLine());
var y = double.Parse(Console.ReadLine());

Нагласимо да се приликом уноса на овај начин очекује да свака унета линија текста садржи по један исправно записан број. Пре броја не може бити размака. Није могуће ни да су у једној линији наведена оба броја. Такав сценарио ће нам у неким задацима бити користан, па ћемо сада приказати како је могуће учитати два броја из исте линије. Рецимо да то уопште није једноставно и да би се разумело подразумева познавање неких напреднијих концепата програмског језика C#. Ипак, можемо га усвојити као идиом, ставити га на “јавно дозвољене пушкице” и користити без детаљног разумевања, све док не обрадимо тему посвећену раду са низовима. Два цела броја бисмо у једном реду учитали на следећи начин.

string[] reci = Console.ReadLine().Split();
int x = int.Parse(reci[0]);
int y = int.Parse(reci[1]);

Три цела броја бисмо из исте линије учитали веома слично.

string[] reci = Console.ReadLine().Split();
int x = int.Parse(reci[0]);
int y = int.Parse(reci[1]);
int z = int.Parse(reci[2]);

У првој линији се учитава линија текста, а онда се помоћу Split она дели на појединачне речи, које се смештају у низ који називамо речи. У овом примеру reci је променљива која је декларисана помоћу string[] чиме се наглашава да она не чува цео учитани текст тј. једну ниску карактера већ низ који садржи неколико ниски тј. речи које се налазе у учитаном тексту. Након тога узимамо једну по једну реч из тог низа (прва се налази на позицији 0, друга на позицији 1 и тако даље) и сваку од њих конвертујемо у цео број и памтимо у одговарајућим променљивама.

На крају, рецимо да је приликом исписа могуће комбиновати текст и бројевне вредности. На пример,

double pi = 3.1415926;
Console.WriteLine("Vrednost broja pi je " + pi);

У овом примеру се наизглед “сабира” текст са реалним бројем. Иза сцене се заправо реални број прво претвори у текст, а онда се добијени текст надовеже на претходни. Слично се дешава и у било којој другој комбинацији бројева и текста приликом исписа - сви наведени бројеви се претварају у текст и надовезују са осталим текстом.

using System;

class Program
{
   static void Main()
   {
   
      Console.Write("Unesi jedan ceo broj: ");
      int ceoBroj = int.Parse(Console.ReadLine());
      Console.Write("Unesi jedan realan broj: ");
      double realanBroj = double.Parse(Console.ReadLine());
      Console.WriteLine("Ceo broj: " + ceoBroj + " " +
                        "Realan broj: " + realanBroj);
   }
}

Основне аритметичке операције и изрази

Ниједан од програма које смо до сада срели није могао бити нарочито интересантан. Могли смо да учитамо податке и да их испишемо у неизмењеном облику.

Рачунар је машина која обрађује податке, тј. која примењујући рачунске операције на основу улазних података добија излазне. У рачунару је све записано помоћу бројева и на најнижем нивоу се све операције своде на основне операције над бројевима. Рачунар или компјутер (енгл. computer) је справа која рачуна тј. справа која је направљена тако да може веома брзо и ефикасно да изводи рачунске операције над бројевима. Рачунање се назива и аритметика (од грчке речи ἀριθμός тј. аритмос која значи број, бројање, рачунање), а рачунске операције се називају и аритметичке операције.

  • Основна аритметичка операција је сабирање. Збир бројева \(3\) и \(5\) се у математици представља као \(3 + 5\). У програмском језику C# користи се идентичан запис \(3 + 5\). Сабирање је применљиво и на целе и на реалне бројеве (а као што смо видели - и на ниске, које се тиме дописују једна на другу). На пример, кôд који учитава и сабира два цела броја може бити написан на следећи начин.

    int x = int.Parse(Console.ReadLine());
    int y = int.Parse(Console.ReadLine());
    Console.WriteLine(x + y);
  • Поред сабирања можемо разматрати одузимање. Разлика бројева \(8\) и \(2\) се у математици представља као \(8 - 2\). У програмском језику C# користи се идентичан запис \(8 - 2\). Одузимање је применљиво и на целе и на реалне бројеве.

  • Још једна од основних операција је и множење. Производ бројева \(4\) и \(6\) се у математици представља као \(4 \cdot 6\). У програмском језику C# множење се означава помоћу оператора * и производ бројева 4 и 6 се записује као 4 * 6.

  • У програмском језику C#, наравно, можемо и да делимо, да израчунавамо остатак при дељењу и цео део количника. Због одређених специфичности ових операција на скупу целих бројева, ми ћемо се у почетку бавити само операцијом дељења реалних бројева, док ћемо се целобројним дељењем детаљније бавити касније. Дељење реалних бројева се врши помоћу оператора / и количник бројева \(7,2\) и \(6,4\) се записује као 7.2 / 6.4. Дељењем два цела броја добија се њихов целобројни количник, док се остатак при дељењу два цела броја може израчунати оператором %. На пример, вредност израза 14 / 4 једнака је 3, а израза 14 % 4 једнака је 2. Ако желимо да одредимо реални количник два цела броја, морамо их представити у реалном облику (на пример, уместо 14/4 пишемо 14.0/4.0). У случају да се деле целобројне променљиве онда вршимо експлицитну конверзију у реалне вредности навођењем (double) испред назива променљиве (нпр. уместо x / y пишемо (double) x / (double) y. Нагласимо да је довољно да било дељеник било делилац буду реални да би се применило реално дељење. У задацима који следе избегаваћемо мешање променљивих различитих типова, а о дубљим везама између реалног и целобројног типа биће више речи у каснијим поглављима.

Слично као и у математици, од константних вредности и променљивих, применом оператора и заграда граде се изрази. Приоритет оператора је усклађен са уобичајеним приоритетом у математици, па је приоритет оператора *, / и % виши од приоритета оператора + и -, док сви наведени оператори имају леву асоцијативност (рачунске операције се изводе с лева на десно). Приоритет и асоцијативност се могу променити навођењем заграда.

У првим програмима ћемо се трудити да приликом извођења операција не мешамо податке различитог типа. Ипак, нагласимо да ако је у изразу један број реалан, а други цео, пре извођења операција се тај цео број претвара у реалан и операција се извршава над реалним бројевима.

Уграђене математичке функције

У многим конкретним применама поред основних аритметичких операција примењују се и неке мало напредније математичке функције и неке чувене математичке константе (нпр. \(\pi\)). У језику C# оне су означене са Math (то су статичке функције класе Math, али у овом тренутку нам та прецизна дефиниција није значајна).

  • Math.Pow(x, y) - израчунава степен \(x^y\), при чему се може применити и за израчунавање корена (не само квадратних) знајући да је \(\sqrt[n]{x} = x^{\frac{1}{n}}\);
  • Math.Sqrt(x) - израчунава квадратни корен \(\sqrt{x}\);
  • Math.Abs(x) - израчунава апсолутну вредност \(|x|\);
  • Math.PI - константа \(\pi\);

Поред ових на располагању су нам и тригонометријске функције (синус, косинус, …), њихове инверзне функције, логаритамска функција, експоненцијална функција и слично, али њих нећемо користити.

Именоване константе

Вредности које су нам потребне у програму, а које се неће мењати током извршавања програма можемо дефинисати у виду именованих константи, које се дефинишу као обичне променљиве, уз навођење кључне речи const пре типа податка, уз обавезну иницијализацију вредности на неку константну вредност. На пример:

const double PI = 3.14159265;

Именованим константама није касније могуће променити вредност у програму.

Дефинисање функција

Поред библиотечких функција које су нам на располагању, програмски језици, па и језик C# програмеру дају могућност да дефинише функције, што доприноси избегавању поновљених делова програма, декомпозицији проблема на мање потпроблеме и бољој организацији кода. Функције можемо замислити слично као у математици. Оне обично за неколико улазних параметара израчунавају једну резултујућу вредност. На пример, наредна функција израчунава обим правоугоника датих страница.

using System;

class Program
{
   // funkcija izračunava obim pravougaonika na osnovu poznatih
   // dužina stranica a i b
   static double Obim(double a, double b)
   {
       // formula za obim pravougaonika
       return 2*a + 2*b;
   }
   
   static void Main()
   {
       // učitavamo dužine stranica pravougaonika
       double a = double.Parse(Console.ReadLine());
       double b = double.Parse(Console.ReadLine());
       // izračunavamo obim pomoću definisane funkcije
       double O = Obim(a, b);
       // ispisujemo izračunati obim
       Console.WriteLine(O);
   }
}

Прва линија static double Obim(double a, double b) се назива декларација функције и у њој се каже да се функција назива Obim (иако није обавезно у језику C# пожељно је да имена функција почињу великим словом), да прима две улазне вредности (два параметра) који су реални бројеви и називају се a и b и да враћа резултат који је такође реалан број. У телу функције (оно је ограничено витичастим заградама) се описује како се резултат израчунава на основу улазних вредности. Када је коначни резултат израчунат, он се враћа на место позива функције кључном речју return. Након исправне дефиниције следи позив функције. У претходном примеру позив је извршен у склопу декларације double O = Obim(a, b). У позиву се наводе аргументи којима се иницијализују параметри функције. У овом случају то су вредности променљивих које су учитане са тастатуре. Аргументи могу бити и константне вредности (параметри морају бити променљиве). На пример, допуштен је позив Obim(5.0, 7.0).

Унутар функције можемо декларисати и користити и променљиве у којима чувамо међурезултате. На пример, можемо дефинисати функцију за израчунавање површине једнакостраничног троугла дате дужине странице.

double PovrsinaJednakostranicnogTrougla(double a)
{
   // izračunavamo visinu trougla
   double h = a * Math.Sqrt(3) / 2.0;
   // izračunavamo površinu na osnovu dužine stranice i visine
   double P = a * h / 2.0;
   // vraćamo konačan rezultat
   return P;
}

Овом функцијом је реализовано израчунавање површине једнакостраничног троугла и када нам год то затреба у програму (а у неком математичком задатку то може бити потребно више пута), можемо само позвати функцију и добити жељени резултат.

Постоје и специјалне функције које не враћају вредност. Оне се некада називају процедуре и једини задатак им је да произведу неки пропратни ефекат (на пример, да нешто испишу на екран). Као повратни тип података наводи се void. На пример, можемо дефинисати процедуру која око датог текста исписује украсне линије, а онда је позвати неколико пута у програму.

using System;

class Program
{
   static void ukrasiTekst(string tekst)
   {
       Console.WriteLine("---------------------------------------");
       Console.WriteLine(tekst);
       Console.WriteLine("---------------------------------------");
   }
   
   static void Main()
   {
      ukrasiTekst("Dobar dan!");
      ukrasiTekst("Zdravo, svima!");
      ukrasiTekst("Dovidjenja!");
   }
}

Више повратних вредности - излазни параметри

Параметри функције се по правилу сматрају њеним улазним вредностима (исто као и у математици), док се вредност враћена помоћу return сматра њеном излазном вредношћу. Функција, дакле, може имати више улазних и само једну излазну вредност (и ово одговара појму функције у математици). Међутим, у програмирању некада имамо потребу да дефинишемо функције које враћају више од једне вредности. То је могуће урадити тако што се користе излазни параметри, који се означавају кључном речју out. На пример, наредна функција конвертује дужину задату само у центиметрима у дужину задату у метрима и центиметрима.

static void UMetreICentimetre(int duzina, 
                              out int metri, out int centimetri)
{
   metri = duzina / 100;
   centimentri = duzina % 100;
}

Позив ове функције се врши на следећи начин.

int metri, centimetri;
UMetreICentimetre(273, out metri, out centimetri);

Након овог позива, вредност променљиве metri биће 2, а центиметри биће 73.

Излазни аргументи, у позиву означени са out морају бити променљиве (не могу бити константе или изрази). Параметри означени са out се не могу читати унутар функције тј. не могу се сматрати њеним улазним параметрима (они су искључиво излазни параметри који служе за враћање резултата).

Поред улазних и излазних параметара постоје и улазно-излазни параметри функције (за које се користи кључна реч ref), али ћемо се њима касније детаљније бавити.